#include <arduino/Arduino.h>
#include <clock/lcd_clock.h>
#include <lcd/qc1602a.h>
#include <utils/scope_timer.h>
#include <push_button/push_button.h>
#include <buzzer/buzzer.h>
#include <shell/simple_shell.h>
#include <beacon/beacon.h>


enum mode_t : unsigned char
{
  NORMAL = 0,
  CHANGE_HOUR_TIMER,
  CHANGE_MINUTE_TIMER,
  CHANGE_SECOND_TIMER,
  CHANGE_HOUR_ALARM,
  CHANGE_MINUTE_ALARM,
  CHANGE_SECOND_ALARM,
  MODE_LAST
};

mode_t& operator++(mode_t& val)
{
  (++reinterpret_cast<unsigned char&>(val)) %= MODE_LAST;
  return val;
}

int main(void)
{
  init();

  lcd::qc1602a* screen = new lcd::qc1602a(12, 11, 10, 9, 2, 3, 4, 5);
  clock::lcd_clock timer(screen, 1, 4);
  clock::lcd_clock alarm(screen, 0, 4);
  alarm.get_start_time().hour = 23;
  alarm.get_start_time().minute = 59;
  alarm.get_start_time().second = 59;

  screen->set_backlight(true);

  push_button::analog_button button1(A0);
  push_button::analog_button button2(A1);
  push_button::analog_button button3(A2);
  push_button::analog_button button4(A3);

  buzzer::buzzer buzz(6);

  beacon::beacon beac(7);
  beac.desactivate();

  bool is_ringing = false;
  mode_t mode = NORMAL;

  shell::simple_shell sh(shell::BR115200);

  auto set_alarm = [&alarm](unsigned char hour, unsigned char minute, unsigned char second)
  {
    alarm.get_start_time().hour = hour;
    alarm.get_start_time().minute = minute;
    alarm.get_start_time().second = second;
    return 0;
  };

  sh.add_function(
        "set_alarm",
        utils::function<int(unsigned char, unsigned char, unsigned char)>(set_alarm));

  while (true)
  {
    {
      utils::scope_timer sctimer(50);
      sh.update();
      timer.update(millis());
      alarm.update(0);
    }

    {
      utils::scope_timer sctimer(150);

      if (button1)
      {
        ++mode;

        switch (mode)
        {
          case CHANGE_HOUR_TIMER:
            timer.blink_hour(true);
            break;
          case CHANGE_MINUTE_TIMER:
            timer.blink_minute(true);
            timer.blink_hour(false);
            break;
          case CHANGE_SECOND_TIMER:
            timer.blink_second(true);
            timer.blink_minute(false);
            break;
          case CHANGE_HOUR_ALARM:
            alarm.blink_hour(true);
            timer.blink_second(false);
            break;
          case CHANGE_MINUTE_ALARM:
            alarm.blink_minute(true);
            alarm.blink_hour(false);
            break;
          case CHANGE_SECOND_ALARM:
            alarm.blink_second(true);
            alarm.blink_minute(false);
            break;
          case NORMAL:
            alarm.blink_second(false);
            break;
          case MODE_LAST:
            break;
        }
      }

      if (button4) is_ringing = false;

      switch (mode)
      {
        case NORMAL:
        case MODE_LAST:
          break;
        case CHANGE_HOUR_TIMER:
        {
          if (button2) timer.get_start_time().increase_hour();
          if (button3) timer.get_start_time().decrease_hour();
          break;
        }
        case CHANGE_MINUTE_TIMER:
        {
          if (button2) timer.get_start_time().increase_minute();
          if (button3) timer.get_start_time().decrease_minute();
          break;
        }
        case CHANGE_SECOND_TIMER:
        {
          if (button2) timer.get_start_time().increase_second();
          if (button3) timer.get_start_time().decrease_second();
          break;
        }
        case CHANGE_HOUR_ALARM:
        {
          if (button2) alarm.get_start_time().increase_hour();
          if (button3) alarm.get_start_time().decrease_hour();
          break;
        }
        case CHANGE_MINUTE_ALARM:
        {
          if (button2) alarm.get_start_time().increase_minute();
          if (button3) alarm.get_start_time().decrease_minute();
          break;
        }
        case CHANGE_SECOND_ALARM:
        {
          if (button2) alarm.get_start_time().increase_second();
          if (button3) alarm.get_start_time().decrease_second();
          break;
        }
      }

      if (is_ringing)
      {
        buzz.play_note('a', 100);
        beac.activate();
      }
      else
      {
        is_ringing = (timer.get_current_time() == alarm.get_start_time());
        beac.desactivate();
      }

      timer.update(millis());
      alarm.update(0);
    }
  }

  return 0;
}
